package com.example.teacherservice.service;

import com.example.teacherservice.entity.Chapter;
import com.example.teacherservice.exception.VideoProcessingException;
import com.example.teacherservice.repository.ChapterRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.*;
import java.util.UUID;

@Service
@RequiredArgsConstructor
public class FileStorageService {

    // 配置视频存储路径（默认值可覆盖）
    @Value("${video.storage.path:D:\\projects\\teacher-service\\video}")
    private String storagePath;

    private final ChapterRepository chapterRepository;


    /**
     * 处理视频上传全流程（同步）
     * @param file 上传的视频文件
     * @param courseId 关联课程ID
     * @return 可访问的视频URL
     * @throws Exception 包含文件存储、时长解析、数据库操作的所有异常
     */
    public String handleVideoUpload(MultipartFile file, Long courseId) throws Exception {
        // 1. 验证文件
        validateVideoFile(file);

        // 2. 存储临时文件
        Path tempFile = storeTempFile(file);

        try {
            // 3. 解析视频时长
            long duration = parseVideoDuration(tempFile);

            // 4. 生成永久存储路径
            String filename = generateUniqueFilename(file.getOriginalFilename());
            Path permanentPath = Paths.get(storagePath, filename);

            // 5. 移动文件到永久位置
            moveToPermanentLocation(tempFile, permanentPath);

            // 6. 生成可访问URL
            String videoUrl = buildVideoUrl(filename);

            // 7. 保存到数据库
            saveChapterToDatabase(courseId, videoUrl, duration, filename);

            return videoUrl;
        } finally {
            // 8. 清理临时文件
            cleanupTempFile(tempFile);
        }
    }

    // =============== 私有方法 ===============

    /**
     * 验证视频文件格式
     */
    private void validateVideoFile(MultipartFile file) throws IllegalArgumentException {
        if (file.isEmpty()) {
            throw new IllegalArgumentException("视频文件不能为空");
        }
        String ext = getFileExtension(file.getOriginalFilename());
        if (!ext.equalsIgnoreCase(".mp4") && !ext.equalsIgnoreCase(".mov")) {
            throw new IllegalArgumentException("仅支持MP4/MOV格式");
        }
    }

    /**
     * 存储临时文件
     */
    private Path storeTempFile(MultipartFile file) throws IOException {
        Path tempDir = Paths.get(storagePath, "temp");
        if (!Files.exists(tempDir)) {
            Files.createDirectories(tempDir);
        }

        String tempFilename = "temp_" + UUID.randomUUID() + getFileExtension(file.getOriginalFilename());
        Path tempFilePath = tempDir.resolve(tempFilename);

        file.transferTo(tempFilePath);
        return tempFilePath;
    }

    /**
     * 解析视频时长（FFprobe）
     */
    private long parseVideoDuration(Path videoPath) throws Exception {
        Process process = new ProcessBuilder(
                "ffprobe",
                "-v", "error",
                "-show_entries", "format=duration",
                "-of", "default=noprint_wrappers=1:nokey=1",
                videoPath.toString()
        ).start();

        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(process.getInputStream()))) {
            String output = reader.readLine();
            return (long) Math.ceil(Double.parseDouble(output)); // 向上取整
        }
    }

    /**
     * 生成唯一文件名
     */
    private String generateUniqueFilename(String originalName) {
        return UUID.randomUUID() + getFileExtension(originalName);
    }

    /**
     * 移动文件到永久位置
     */
    private void moveToPermanentLocation(Path source, Path target) throws IOException {
        Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
    }

    /**
     * 构建视频访问URL
     */
    private String buildVideoUrl(String filename) {
        return "/videos/" + filename; // 根据实际部署调整（如CDN地址）
    }

    /**
     * 保存章节到数据库
     */
    private void saveChapterToDatabase(Long courseId, String url, long duration, String filename) {
        Chapter chapter = new Chapter();
        chapter.setCourseId(courseId);
        chapter.setUrl(url);
        chapter.setDuration(duration);
        chapter.setTitle(filename.replace(getFileExtension(filename), ""));
        chapterRepository.save(chapter);
    }

    /**
     * 清理临时文件
     */
    private void cleanupTempFile(Path file) {
        try {
            Files.deleteIfExists(file);
        } catch (IOException e) {
            System.err.println("[警告] 临时文件删除失败: " + file);
        }
    }

    /**
     * 获取文件扩展名
     */
    private String getFileExtension(String filename) {
        return filename.substring(filename.lastIndexOf('.'));
    }

    /**
     * 解析远程视频时长（带重试机制）
     */
    public long parseVideoDuration(String videoUrl) throws VideoProcessingException {
        int retryCount = 0;
        while (retryCount < 3) {
            try {
                Process process = new ProcessBuilder(
                        "ffprobe",
                        "-v", "error",
                        "-show_entries", "format=duration",
                        "-of", "default=noprint_wrappers=1:nokey=1",
                        videoUrl
                ).start();

                try (BufferedReader reader = new BufferedReader(
                        new InputStreamReader(process.getInputStream()))) {

                    String output = reader.readLine();
                    if (output == null) throw new VideoProcessingException("FFprobe无输出");

                    double seconds = Double.parseDouble(output);
                    return (long) Math.ceil(seconds); // 向上取整
                }
            } catch (IOException | NumberFormatException e) {
                retryCount++;
                System.out.println("视频解析失败[尝试 {}次] URL: " +videoUrl);

                if (retryCount >= 3) {
                    throw new VideoProcessingException("视频解析最终失败: " + e.getMessage());
                }

                try {
                    Thread.sleep(1000 * retryCount); // 指数退避
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                }
            }
        }
        return 0L;
    }
}